﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Drawing;

namespace DirectX_CS
{   
    public delegate float PolarZFunc(float theta);
	public delegate Vector3 PolarVec3Func(float theta);

    public class TriangleFan : IManagedVertexBuffer, IManagedMaterial, 
		IManagedModelTransformable, IColorable, IDisposable
    {

        private FloatFunc mPolarZFunc = null;
        private FloatFunc mPolarXFunc = null;
        private FloatFunc mPolarYFunc = null;
		private PolarVec3Func mPolarVFunc = null;

        public event VBChangedDelegate VertexBufferChanged;

        protected SVector[] vertexData = null;
		[PGBrowsable]
		public VertexFormats VertexFormat
		{
			get { return SVector.Format; }
		}
		protected bool mEnableCallRenderPrimitives = true;
		[PGBrowsable]
		public bool EnableCallRenderPrimitives
		{
			get { return mEnableCallRenderPrimitives; }
			set { mEnableCallRenderPrimitives = value; }
		}
		protected bool mEnableCallRenderIndexedPrimitives = true;
		[PGBrowsable]
		public bool EnableCallRenderIndexedPrimitives
		{
			get { return mEnableCallRenderIndexedPrimitives; }
			set { mEnableCallRenderIndexedPrimitives = value; }
		}
        protected int mAngleIterations = 64;
		[PGBrowsable]
        public int AngleIterations
        {
            get { return mAngleIterations; }
			set { mAngleIterations = value; }
        }
        protected float mRadius = 1;
		[PGBrowsable]
        public float Radius
        {
            get {return mRadius;}
            set { mRadius = value;}
        }
        protected float mAMax = (2f * (float)Math.PI);
		[PGBrowsable]
        public float AMax
        {
            get { return mAMax; }
			set { mAMax = value; }
        }
        protected float mAMin = 0;
		[PGBrowsable]
        public float AMin
        {
            get { return mAMin; }
			set { mAMin = value; }
        }
		protected Color mDefaultColor = Color.FromArgb(255, Color.ForestGreen);
		[PGBrowsable]
		public Color MainVertexColor
		{
			get { return mDefaultColor; }
			set { mDefaultColor = value;
				//Recreate();
			}
		}

        protected Vector3 mStartCenterPoint;
		[PGBrowsable]
        public Vector3 StartCenterPoint
        {
            get { return mStartCenterPoint; }
			set { mStartCenterPoint = value; }
        }

        protected static float mDefaultX = 0f;
        protected static float mDefaultY = 0f;
        protected static float mDefaultZ = 0f;

        public static float DefaultPolarZFunc(float theta) { return 0; }

        protected Material mMaterial;
		[PGBrowsable]
		public Material Material
        {
			get { return mMaterial; }
			set { mMaterial = value; }
        }
        protected void InitializeMaterial()
        {
            mMaterial = new Material();
            mMaterial.Specular = Color.FromArgb(122,255,255,255);
            mMaterial.Ambient = Color.FromArgb(122,255,255,255);
            mMaterial.Diffuse = Color.FromArgb(122,255,255,255); 
        }
		public TriangleFan()//, Cartesian3dSurface matchDataObject)
		{
			InitializeMaterial();
			mStartCenterPoint = new Vector3(0, 0, 0);
			mAMin = 0;
			mAMax = (2f * (float)Math.PI);
			CreateVertexData();//radius, mAMin, mAMax, mAngleIterations, null, null, null, mDefaultColor, mStartCenterPoint);
			PrimitiveManager.Register(this);
			VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.
		}
        public TriangleFan(float radius)//, Cartesian3dSurface matchDataObject)
        {
            InitializeMaterial();
            mStartCenterPoint = new Vector3(0, 0, 0);
            mAMin = 0;
            mAMax = (2f * (float)Math.PI);
			mRadius = radius;

			CreateVertexData();//radius, mAMin, mAMax, mAngleIterations, null, null, null, mDefaultColor, mStartCenterPoint);
			PrimitiveManager.Register(this);
            if (VertexBufferChanged != null)
                VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.

        }
        public TriangleFan(float radius, Vector3 start_center_point)//, Cartesian3dSurface matchDataObject)
        {
            InitializeMaterial();
            mAMin = 0;
            mAMax = (2f * (float)Math.PI);
			mRadius = radius;
			mStartCenterPoint = start_center_point;

			CreateVertexData();//radius, mAMin, mAMax, mAngleIterations, null, null, null, mDefaultColor, start_center_point);
			PrimitiveManager.Register(this);
            if (VertexBufferChanged != null)
                VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.
  
        }
		public TriangleFan(float radius, float zDefault)//, Cartesian3dSurface matchDataObject)
		{
			InitializeMaterial();
			mDefaultZ = zDefault;
			mRadius = radius;
			CreateVertexData();//radius, mAMin, mAMax, mAngleIterations, null, null, null, mDefaultColor, mStartCenterPoint);

			PrimitiveManager.Register(this);
			if (VertexBufferChanged != null)
				VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.

		}
        public TriangleFan(FloatFunc polarZFunc, float radius, Vector3 start_center_point, int angleIterations, Vector3 moveToPosition)//, Cartesian3dSurface matchDataObject)
        {
            mPolarZFunc = polarZFunc;
            InitializeMaterial();
            mAMin = 0;
            mAMax = (2f * (float)Math.PI);
            mAngleIterations = angleIterations;
			mRadius = radius;
			mStartCenterPoint = start_center_point;

            CreateVertexData();
			PrimitiveManager.Register(this);
			VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.
        }

        public TriangleFan(FloatFunc polarXFunc, FloatFunc polarYFunc, FloatFunc polarZFunc, Vector3 start_center_point, int angleIterations, float angleMinimum, float angleMaximum)//, Cartesian3dSurface matchDataObject)
        {
            mPolarZFunc = polarZFunc;
            mPolarXFunc = polarXFunc;
            mPolarYFunc = polarYFunc;
            InitializeMaterial();
            mAMin = angleMinimum;
            mAMax = angleMaximum;
            mAngleIterations = angleIterations;
			mStartCenterPoint = start_center_point;
            //radius is not used so just pass a 1
            CreateVertexData();//1, mAMin, mAMax, angleIterations, mPolarXFunc, mPolarYFunc, mPolarZFunc, mDefaultColor, start_center_point);
			PrimitiveManager.Register(this);
			VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.
        }

		public TriangleFan(PolarVec3Func heightFunc, Vector3 start_center_point, bool visible)//, Cartesian3dSurface matchDataObject)
		{
			mPolarVFunc = heightFunc;
			InitializeMaterial();
			mStartCenterPoint = start_center_point;
			//radius is not used so just pass a 1
			CreateVertexData();//1, mAMin, mAMax, angleIterations, mPolarXFunc, mPolarYFunc, mPolarZFunc, mDefaultColor, start_center_point);
			EnableCallRenderPrimitives = visible;
			PrimitiveManager.Register(this);
			VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.
		}

        public TriangleFan(float radius, Vector3 start_center_point, float angleMinimum, float angleMaximum, int angleIterations)//, Cartesian3dSurface matchDataObject)
        {
            InitializeMaterial();
            mAMin = angleMinimum;
            mAMax = angleMaximum;
            mAngleIterations = angleIterations;
            mStartCenterPoint = start_center_point;
			mRadius = radius;
            //radius is not used so just pass a 1
            CreateVertexData();
			PrimitiveManager.Register(this);
			VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.
        }
        public TriangleFan(float radius, float angleMinimum, float angleMaximum, int angleIterations)//, Cartesian3dSurface matchDataObject)
        {
            InitializeMaterial();
            mAMin = angleMinimum;
            mAMax = angleMaximum;
            mAngleIterations = angleIterations;
            mStartCenterPoint = new Vector3(0,0,0);
			mRadius = radius;
                        
			CreateVertexData();
			PrimitiveManager.Register(this);
			VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.
        }
		public void Recreate()
		{
			CreateVertexData();
			VertexBufferChanged(this, vertexData.Length); //as soon as its registered calls will probably start arriving.
		}
		public void UpdateTransform()
		{
			VertexBufferChanged(this, vertexData.Length);
		}
        ~TriangleFan()
        {
			DisposeInstance();
        }

		public void DisposeInstance()
		{
			UnRegister();
		}
        public void UnRegister()
        {
			PrimitiveManager.Unregister(this); //cannot hurt to unregister.
        }
        private static float cosf(double d)
        {
            return (float)Math.Cos(d);
        }
        private static double cos(double d)
        {
            return Math.Cos(d);
        }
        private static double sin(double d)
        {
            return (float)Math.Sin(d);
        }
        private static float sinf(double d)
        {
            return (float)Math.Sin(d);
        }
        public void CreateVertexData()
        {
			float radius = mRadius;
			float amin = mAMin; float amax = mAMax;
			int AngleIterations = mAngleIterations;
			FloatFunc polarXFunc = mPolarXFunc;
			FloatFunc polarYFunc = mPolarYFunc;
			FloatFunc polarZFunc = mPolarZFunc;
			PolarVec3Func polarVFunc = mPolarVFunc;

			Color c = mDefaultColor;
			Vector3 firstPoint_Center = mStartCenterPoint;

            int color = c.ToArgb();
            SVector[] dataVerts = new SVector[AngleIterations + 2];

			if (firstPoint_Center != null)
				dataVerts[0] = new SVector(new Vector3(firstPoint_Center.X, firstPoint_Center.Y, firstPoint_Center.Z), new Vector3(0, 0, 0), color); //position of tran of circle
			else
			{
				dataVerts[0] = new SVector(0f, 0f, mDefaultZ, 0, 0, 0, color);
			}
            float a = 0;
            float x,y,z;
            float radiansPerIteration = (amax - amin) / AngleIterations;
            for (int dataVertIdx = 0; dataVertIdx <= AngleIterations; dataVertIdx++)
            {
                a = (float)(dataVertIdx * radiansPerIteration);
                //float nx,ny,nz;
                //nx = ny = nz = 0;
                //add one for the point we added earlier

                
				if (polarVFunc != null)
				{
					Vector3 pos = mPolarVFunc(a);
					x = pos.X; y = pos.Y; z = pos.Z;
				}
				else
				{
					if (polarXFunc != null)
					{
						x = polarXFunc(a);

						if (polarZFunc != null)
							z = polarZFunc(a);
						else //if  (polarZFunc == null)
							z = mDefaultZ; 

						if (polarYFunc != null)
						{
							y = polarYFunc(a);
						}
						else
						{
							y = mDefaultY;
						}
					}
					else  //if(polarXFunc == null)
					{
						if (polarYFunc != null)
						{
							x = mDefaultX;
							y = polarYFunc(a);
						}
						else
						{
							x = radius * cosf(a);
							y = radius * sinf(a);
						}

						if (polarZFunc != null)
							z = polarZFunc(a);
						else  //if  (polarZFunc == null)
							z = mDefaultZ;
					} 
				}

                dataVerts[dataVertIdx + 1] = new SVector(x, y, z, 0,0,0, color);
            }

            ///BEGIN: BE SURE TO CALL... WHEN HAVE NEW DATA; OTHERWISE THE DATA WILL ONLY GET QUERIED ONCE.
            vertexData = dataVerts;
            ///END: BE SURE TO CALL... AT END OF DATA CREATION. 
            if (VertexBufferChanged != null)
                VertexBufferChanged(this, vertexData.Length);
        }

		public Array GetVertices()
        {
			return (Array)vertexData;
        }

        public int GetVerticesCount()
        {
            return vertexData.Length;
        }

        /// <summary>
        /// Since each primitiveWithVertices should for diversity be in charge of its own representation
        /// in rendering, although the memory will be managed.
        /// </summary>
        public void RenderPrimitives(PrimitiveManager caller, int dataLength)
        {   
            MainProgram.DxDevice.DrawPrimitives(PrimitiveType.TriangleFan, 0, dataLength - 2); //was dataLength-1);
        }

        long[] GetIndices()
        {
            return null;
        }

        public void ManagerDestroyed(PrimitiveManager caller)
        {
        }


		#region IModelTransformable Members

		TransformMatrix modelView = new TransformMatrix();
		public TransformMatrix ModelView
		{
			get
			{
				return modelView;
			}
			set
			{
				modelView = value;
			}
		}

		#endregion
	}//end of class..
}//end of namespace.

